#ifndef PORTABLE_PRIMITIVES_H_
#define PORTABLE_PRIMITIVES_H_

#include <string>
#include <type_traits>

#include "src/pbc/portable-logging.h"
#include "include/v8-internal.h"

namespace portable {

class PortableBigInt {
 public:
  explicit PortableBigInt(const char* bigint) : bigint_(bigint) {}
  PortableBigInt() = default;

  const char* c_str() const { return bigint_.c_str(); }
  const std::string &str() const { return bigint_; }

 private:
  std::string bigint_;  // expect no tailing 'n'
};

class Smi {
 public:
  // Convert a Smi to an int.
  static inline int ToInt(const Smi &in) {
    return in.value();
  }

  // Convert a value to a Smi.
  static inline Smi FromInt(int value) {
    return Smi(value);
  }

  template <typename E,
            typename = typename std::enable_if<std::is_enum<E>::value>::type>
  static inline Smi FromEnum(E value) {
    static_assert(sizeof(E) <= sizeof(int));
    return FromInt(static_cast<int>(value));
  }

  static inline Smi zero() { return Smi::FromInt(0); }
  static constexpr int kMinValue = v8::internal::kSmiMinValue;
  static constexpr int kMaxValue = v8::internal::kSmiMaxValue;

  // Returns whether value can be represented in a Smi.
  template <typename T>
  static inline std::enable_if_t<std::is_integral_v<T> && std::is_signed_v<T>,
                                 bool> constexpr IsValid(T value) {
    return value >= kMinValue && value <= kMaxValue;
  }
  template <typename T>
  static inline std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>,
                                 bool> constexpr IsValid(T value) {
    return value <= kMaxValue;
  }

  struct SmiHasher {
    size_t operator()(const Smi& v) const {
      return v.value();
    }
  };

  struct SmiEqual {
    bool operator()(const Smi& lhs, const Smi& rhs) const {
      return lhs.value() == rhs.value();
    }
  };

  inline int value() const { return value_; }
 private:
  explicit Smi(int val) : value_(val) {
    P_DCHECK(val <= kMaxValue && val >= kMinValue);
  }
  int value_;
};

}  // namespace portable


#endif  // PORTABLE_PRIMITIVES_H_
